﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.ComponentModel;
using System.Globalization;
using System.Collections;

namespace MassIntellect.WorkIt
{
	public abstract class WorkFrame : Pipe, IWorkFrame
    {
        #region IWorkFrame Members
        //public  bool    WorkFrameResultOk;

        [Browsable(true)]
		[Editor(typeof(Design.CollectionEditor<ILogHandlerKeeper>), typeof(System.Drawing.Design.UITypeEditor))]
		public ICollection Logs
		{
			get
			{
				return OnLog != null
						   ? OnLog.GetInvocationList().Select(s => s.Target).OfType<ILogHandlerKeeper>().ToArray()
						   : null;
			}
			set
			{
				OnLog = null;
				if (value != null)
					foreach (var item in value.OfType<ILogHandlerKeeper>())
						OnLog += item.LogHandler;
			}
		}

		[Browsable(true)]
		[Editor(typeof(Design.CollectionEditor<IWarningHandlerKeeper>), typeof(System.Drawing.Design.UITypeEditor))]
		public ICollection Warnings
		{
			get
			{
				return OnWarning != null
						   ? OnWarning.GetInvocationList().Select(s => s.Target).OfType<IWarningHandlerKeeper>().ToArray
								 ()
						   : null;
			}
			set
			{
				OnWarning = null;
				if (value != null)
					foreach (var item in value.OfType<IWarningHandlerKeeper>())
						OnWarning += item.WarningHandle;
			}
		}

		[Browsable(true)]
		[Editor(typeof(Design.CollectionEditor<IExceptionHandlerKeeper>), typeof(System.Drawing.Design.UITypeEditor))]
		public ICollection Exceptions
		{
			get
			{
				return OnExcept != null
						   ? OnExcept.GetInvocationList().Select(s => s.Target).OfType<IExceptionHandlerKeeper>().
								 ToArray()
						   : null;
			}
			set
			{
				OnExcept = null;
				if (value != null)
					foreach (var item in value.OfType<IExceptionHandlerKeeper>())
						OnExcept += item.ExceptionHandler;
			}
		}

		[Browsable(true)]
		[Description("Задействованность кадра.")]
		[DefaultValue(false)]
		[Serialize(SerializeAs.Attribute)]
		public virtual bool IsEnabled { get; set; }

		/// <summary>
		/// Сквозная передача в случае если кадр отключен
		/// </summary>
		[Browsable(true)]
		[Description("Сквозная передача сигнала в случае если кадр отключен.")]
		[DefaultValue(false)]
		[Serialize(SerializeAs.Attribute, IgnoreDefault = true)]
		public bool IsPulseIfDisabled { get; set; }

		[Browsable(false)]
		[Serialize(SerializeAs.Attribute, IgnoreDefault = true)]
		public string ExceptionPipe
		{
			get
			{
				return ExcPipe.Name == "" ? null : ExcPipe.Name;
			}
			set
			{
				ExcPipe.Name = value == "" ? null : value;
				ExcPipe.IsActive = !value.IsEmpty();
			}
		}
		private readonly Pipe ExcPipe = new Pipe { Scope = Scope.Public, IsActive = false };

		[Serialize(SerializeAs.Attribute, IgnoreDefault = true)]
		[Browsable(true)]
		[Description("Вызывать исключение в случае его возникновения в работе кадра.")]
		[DefaultValue(false)]
		[Category("Обработка исключений")]
		public virtual bool IsThrowException { get; set; }

		[Serialize(SerializeAs.Attribute, IgnoreDefault = true)]
		[Browsable(false)]
		public virtual Guid Id { get; set; }

		[Serialize(SerializeAs.Attribute, IgnoreDefault = true)]
		[Browsable(true)]
		[Description("Автоматическое отключение кадра в случае возникновения исключения.")]
		[DefaultValue(false)]
		[Category("Обработка исключений")]
		public virtual bool IsAutoDisableOnException { get; set; }

		[Browsable(false)]
		[Description("Состояние кадра.")]
		[DefaultValue(false)]
		public virtual State State { get; set; }

		protected Condition _Condition;
		[Browsable(false)]
		public virtual Condition Condition
		{
			get
			{
				return _Condition ?? Condition.Templates.TRUE;
			}
			set { _Condition = value; }
		}

		public virtual void SetUp(XElement xe)
		{
			XmlSerializeProvider.SetUp(this, xe);
			XElement x;

			if ((x = xe.Element("Warnings")) != null)
				foreach (var item in x.Elements())
					XmlSerializeProvider.Deserialize<IWarningHandlerKeeper>(item, null,
																		obj => OnWarning += ((IWarningHandlerKeeper)obj).WarningHandle);

			if ((x = xe.Element("Logs")) != null)
				foreach (var item in x.Elements())
					XmlSerializeProvider.Deserialize<ILogHandlerKeeper>(item, null,
																		obj => OnLog += ((ILogHandlerKeeper)obj).LogHandler);

			if ((x = xe.Element("Exceptions")) != null)
				foreach (var item in x.Elements())
					XmlSerializeProvider.Deserialize<IExceptionHandlerKeeper>(item, null,
																		obj => OnExcept += ((IExceptionHandlerKeeper)obj).ExceptionHandler);

		}

		public virtual XElement Serialize()
		{
			XElement xe = XmlSerializeProvider.SerializeOnlyPropertyAndType(this, false);
			XElement[] xa;

			xe.Add(OnWarning != null && (xa = OnWarning.GetInvocationList()
									.Select(s => s.Target)
									.OfType<IWarningHandlerKeeper>()
									.Select(s => XmlSerializeProvider.Serialize(s, (bool?)null))
									.ToArray()).Length > 0
				? new XElement("Warnings", xa)
				: null);

			xe.Add(OnLog != null && (xa = OnLog.GetInvocationList()
									.Select(s => s.Target)
									.OfType<ILogHandlerKeeper>()
									.Select(s => XmlSerializeProvider.Serialize(s, (bool?)null))
									.ToArray()).Length > 0
				? new XElement("Logs", xa)
				: null);

			xe.Add(OnExcept != null && (xa = OnExcept.GetInvocationList()
									.Select(s => s.Target)
									.OfType<IExceptionHandlerKeeper>()
									.Select(s => XmlSerializeProvider.Serialize(s, (bool?)null))
									.ToArray()).Length > 0
				? new XElement("Exceptions", xa)
				: null);

			return xe;
		}

		[Browsable(false)]
		[Description("Доступные каналы.")]
		public virtual IEnumerable<IPipe> Pipes
		{
			get
			{
				yield return this;
				if (ExcPipe.IsActive)
					yield return ExcPipe;
			}
		}

		public virtual IPipe this[string nameIndex]
		{
			get
			{
				if (nameIndex.IsEmpty())
					return this;
				if (ExcPipe.IsActive && ExcPipe.Name == nameIndex)
					return ExcPipe;
				return Null;
			}
		}

		protected virtual void protectedPulse(PulseArgs args)
		{
			ThroughPulse(args);
		}

		/// <summary>
		/// Сквозная передача
		/// </summary>
		/// <param name="args"></param>
		protected void ThroughPulse(PulseArgs args)
		{
#if DEBUG
			//Echo("Передача {0}", args.Id);
#endif
	    
            base.Pulse(args);
		}

		public override void Pulse(PulseArgs args)
		{
#if !DEBUG
			try
			{
#endif            
			if (IsEnabled)
				protectedPulse(args);
			else if (IsPulseIfDisabled)
				ThroughPulse(args);
#if !DEBUG
			}
			catch (Exception ex)
			{
				ThrowException(ex);
			}
#endif
		}

		protected void Log(string strk, params object[] args)
		{
			strk = strk.goFormat(args);
			if (OnLog != null)
				OnLog(this, strk);
		}

		private bool Exception(Exception ex, bool isThrow)
		{
			var res = false;
			if (OnExcept != null)
				res = OnExcept(this, ex);
			if (IsAutoDisableOnException)
				IsEnabled = false;
#if !DEBUG
			if (isThrow)
				throw ex;
			else
				return res;
#else
			throw ex;
#endif
		}

		protected string GetKeyOrDefault(string key, string dflt)
		{
			return key.IsEmpty() ? dflt : key;
		}

		/// <summary>
		/// Вызов исключения
		/// </summary>
		/// <param name="ex"></param>
		protected void ThrowException(Exception ex)
		{
			if (ExcPipe.IsActive)
				ExcPipe.Pulse(new PulseArgs(this).AddParam(ex.Message, ex));
			Exception(ex, IsThrowException);
		}

		protected void Echo(string strk, params object[] args)
		{
			if (OnEcho != null)
				OnEcho(this, strk.goFormat(args));
		}

		protected bool Warning(string strk, string mnemoCode, params object[] args)
		{
			Log("Предупреждение: '{0}'. {1}", mnemoCode, strk.goFormat(args));
			var res = true;
			if (OnWarning != null)
				res = OnWarning
						.GetInvocationList()
						.Cast<Func<IPipe, string, object[], string, bool>>()
						.Aggregate(res, (current, item) => current & item(this, mnemoCode, args, strk.goFormat(args)));
			return res;
		}

		public virtual event Action<IPipe, string> OnLog;

		public virtual event Action<IPipe, string> OnEcho;

		public virtual event Func<IPipe, string, object[], string, bool> OnWarning;

		public virtual event Func<IPipe, Exception, bool> OnExcept;

		[Browsable(true)]
		[Description("Возможность добавления новых каналов.")]
		[DefaultValue(false)]
		[Serialize(SerializeAs.Attribute)]
		public virtual bool IsAllowAddingPipe { get; set; }

		[Browsable(true)]
		[Description("Возможность удаления существующих каналов.")]
		[DefaultValue(false)]
		[Serialize(SerializeAs.Attribute)]
		public virtual bool IsAllowRemovePipe { get; set; }

		public virtual void AddPipe(string key)
		{

		}

		public virtual bool RemovePipe(string key)
		{
			return false;
		}

		#endregion
	}

	public class CollectionTypeConverter : TypeConverter
	{
		/// <summary>
		/// Только в строку
		/// </summary>
		public override bool CanConvertTo(
		  ITypeDescriptorContext context, Type destType)
		{
			return destType == typeof(string);
		}

		/// <summary>
		/// И только так
		/// </summary>
		public override object ConvertTo(
		  ITypeDescriptorContext context, CultureInfo culture,
		  object value, Type destType)
		{
			return "(Список)";
		}
	}
}
